﻿using System.Net;
using System.Net.Sockets;

namespace TcpTool
{
    /// <summary>
    /// Tcp 客户端
    /// </summary>
    public class TCPClient
    {
        public TcpClient? tcpClient { get; set; }

        public Action<bool>? IsClientConnect; // 客户端连接状态
        public Action<byte[]>? DataReceived; // 接收到数据

        public TCPClient()
        {
            tcpClient = new TcpClient();
        }

        /// <summary>
        /// 客户端连接到服务端
        /// </summary>
        /// <param name="serverIP"></param>
        /// <param name="serverPort"></param>
        /// <returns></returns>
        public async Task<bool> ConnectAsync(IPAddress serverIP, int serverPort)
        {
            #region 客户端连接到服务端

            try
            {
                tcpClient = new TcpClient();
                await tcpClient.ConnectAsync(serverIP, serverPort).ConfigureAwait(false);

                // 连接成功后返回true
                if (tcpClient.Connected)
                {
                    IsClientConnect?.Invoke(true);

                    _ = Task.Run(() => ReceiveData());

                    // 启动定时检查连接状态的任务
                    _ = Task.Run(() => CheckConnection());
                    return true;
                }
                else
                {
                    IsClientConnect?.Invoke(false);
                    Console.WriteLine("Connection failed.");
                    return false;
                }
            }
            catch (Exception e)
            {
                IsClientConnect?.Invoke(false);
                Console.WriteLine("Exception: " + e.Message);
                return false;
            }

            #endregion
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <returns></returns>
        private async Task ReceiveData()
        {
            #region 接收数据

            byte[] buffer = new byte[8192];
            NetworkStream stream = tcpClient!.GetStream();

            while (true)
            {
                int bytesRead = await stream.ReadAsync(buffer).ConfigureAwait(false);

                if (bytesRead > 0)
                {
                    byte[] receivedData = new byte[bytesRead];
                    Array.Copy(buffer, receivedData, bytesRead);
                    DataReceived?.Invoke(receivedData); // 以byte[]形式触发事件

                    Array.Clear(buffer, 0, buffer.Length); // 清空缓冲区
                }
            }

            #endregion
        }

        /// <summary>
        /// 心跳包检查连接状态
        /// 在客户端检查当前连接状态
        /// </summary>
        /// <returns></returns>
        private async Task CheckConnection()
        {
            #region 心跳包检查连接状态

            try
            {
                while (true)
                {
                    await Task.Delay(500); // 每500ms检查一次连接状态

                    // 检查连接状态
                    if (tcpClient == null || !tcpClient.Connected)
                    {
                        IsClientConnect?.Invoke(false);
                        Console.WriteLine("Server closed the connection.");
                        break;
                    }

                    // 检查服务端是否关闭连接
                    if (tcpClient.Client.Poll(0, SelectMode.SelectRead) && tcpClient.Client.Available == 0)
                    {
                        IsClientConnect?.Invoke(false);
                        Console.WriteLine("Server closed the connection.");
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception while checking connection: " + ex.Message);
            }

            #endregion
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendDataAsync(byte[] data)
        {
            #region 发送数据

            try
            {
                NetworkStream stream = tcpClient?.GetStream()!;
                await stream.WriteAsync(data).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception while sending data: " + ex.Message);
            }

            #endregion
        }

        /// <summary>
        /// 客户端断开连接
        /// </summary>
        public void Disconnect()
        {
            #region 客户端断开连接

            try
            {
                tcpClient?.Close();
                IsClientConnect?.Invoke(false);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            #endregion
        }
    }
}
